import bpy
from typing import List
from ...addon.naming import FluidLabNaming
from bpy.types import PropertyGroup, UIList, Object, Collection, Context
from .common_list_methods import FLUIDLAB_CommonList
from ...libs.functions.basics import set_active_object, rm_ob
from ...libs.functions.collections import remove_collection_if_is_empty
from ...libs.functions.get_common_vars import get_common_vars
from bpy.props import StringProperty, IntProperty, CollectionProperty, IntProperty, BoolProperty, PointerProperty
from .basic_store_objects import StoredObjects
from .active_collider_props.props_collider_settings import ActiveColliderSettingsProps
from .active_collider_props.props_collider_animation import ActiveColliderAnimationProps


""" Fluids Colliders List """


class FLUIDLAB_UL_draw_fluids_colliders(UIList):

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
        layout.use_property_split = False
        ui = get_common_vars(context, get_ui=True)

        check_if_have_colliders = len(item.colliders) <= 0

        if not item or not item.id_name or check_if_have_colliders:
            layout.row().prop(item, "remove", text="Clear", icon='X')
            return
            
        if any(item_collider.ob.name not in context.view_layer.objects for item_collider in item.colliders if item_collider.ob is not None):
            layout.row().prop(item, "remove", text="Clear", icon='X')
            return

        left_sect = layout.row(align=True)

        # Si solo es un objeto le cambio el icono aunque sea tipo list:
        colliders = [c.ob for c in item.colliders]
        if colliders:
            icon = FluidLabNaming.COLLIDER_TYPE_LIST if len(colliders) > 1 else FluidLabNaming.COLLIDER_TYPE_OB
        else:
            icon = item.item_type

        left_sect.prop(item, "label_txt", text="", emboss=False, icon=icon)

        # Right Section:
        right_sect = left_sect.row(align=True)
        # right_sect.alignment = 'RIGHT'
        
        # icono ojito:
        right_sect.prop(item, "hide_set", text="", toggle=True, icon='HIDE_ON' if not item.hide_set else 'HIDE_OFF')

        if ui.main_modules == 'COLLIDERS':

            # icono monitor:
            right_sect.prop(item, "use_collision", text="", toggle=True, icon='RESTRICT_VIEW_OFF' if item.use_collision else 'RESTRICT_VIEW_ON')        

            # Remove icon:
            rm_button = right_sect.row(align=True)
            rm_button.alert = True
            rm_button.prop(item, "remove", text="", emboss=False, icon='X', icon_only=True)


class FluidCollidersListItem(PropertyGroup):

    label_txt: StringProperty(name="Name")
    id_name: StringProperty(name="ID")
    item_type: StringProperty(default="")
    single_mode: BoolProperty(default=False)
    gn_mode: BoolProperty(default=False)

    # para almacenar los colliders:
    colliders: CollectionProperty(type=StoredObjects)
    def add_collider(self, ob:Object) -> None:
        if not any(ob_item.ob == ob for ob_item in self.colliders):
            item = self.colliders.add()
            item.ob = ob

    def do_remove(self, context):

        fluid_colliders, fluid_single_colliders = get_common_vars(context, get_fluid_colliders=True, get_fluid_single_colliders=True)

        # Eliminamos los modifier:

        for c in self.colliders:

            ob = c.ob
            if ob is not None:
                
                # Si es de tipo GN:
                if self.gn_mode:
                    
                    # Desocultamos los originales:
                    gn_mod = ob.modifiers.get(FluidLabNaming.COLLISION_MOD_GN)
                    if gn_mod:
                        for node in gn_mod.node_group.nodes:
                            if node.bl_idname == 'GeometryNodeCollectionInfo':
                                collecion = node.inputs["Collection"].default_value
                                if collecion:
                                    for org_ob in collecion.objects[:]:
                                        org_ob.hide_set(False)
                    
                    # Y finalmente eliminamos el GN ob:
                    rm_ob(context, ob)
                    remove_collection_if_is_empty(context, FluidLabNaming.GN_COLLIDERS)

                else:

                    # Si es de tipo normal:
                    next((ob.modifiers.remove(mod) for mod in ob.modifiers if mod.type == 'COLLISION'), None)

        # Eliminamos las fuerzas del collider si las tuviera:
        bpy.ops.fluidlab.forces_collliders_rm()

        # --- Start single_collisions ----
        #-------------------------------------------------------------------------------------------------------------
        # Eliminamos los canvas de los items del listado de single_collisions (Dinamic Paint y Force Colliders):
        dp_items = fluid_single_colliders.get_all_items_with_id(self.id_name)
        for item in dp_items:
            ob = item.collider
            for dp_mod in ob.modifiers:
                if dp_mod.type != 'DYNAMIC_PAINT' and FluidLabNaming.DP_CANVAS_MOD not in dp_mod.name:
                    continue
                ob.modifiers.remove(dp_mod)

        # Lo eliminamos tambien del listado de Dinamic Paint Canvas (que es copartido por el Forces Collision también)
        fluid_single_colliders.remove_item(self.id_name)
        #-------------------------------------------------------------------------------------------------------------
        # --- End single_collisions ----
        
        # Lo eliminamos del listado:
        fluid_colliders.remove_item(self.id_name)
        fluid_colliders.list_index = fluid_colliders.length-1 if not fluid_colliders.is_void else 0

    remove: BoolProperty(
        default=False, 
        update=do_remove
    )
    
    def use_collision_update(self, context):
        # Activamos/Desactivamos el ojito del collision:
        for c in self.colliders:
            ob = c.ob
            ob.collision.use = self.use_collision

    use_collision: BoolProperty(
        name="Use Collision",
        description="Enable/Disable Collisions",
        default=True,
        update=use_collision_update
    )

    def hide_set_update(self, context):
        # Ocultamos/Desocultamos el ojito del collider:
        for c in self.colliders:
            ob = c.ob
            ob.hide_set(not self.hide_set)

    hide_set: BoolProperty(
        name="Hide Colliders",
        description="Hide/Unhide Colliders",
        default=True, 
        update=hide_set_update
    )

    item_type: StringProperty(default="")

    #-------------------------------------------------------------------------
    # Propiedades guardadas en el active_collider.settings.x:
    #-------------------------------------------------------------------------
    settings: PointerProperty(type=ActiveColliderSettingsProps)

    #-------------------------------------------------------------------------
    # Propiedades guardadas en el active_collider.animation.x:
    #-------------------------------------------------------------------------
    animation: PointerProperty(type=ActiveColliderAnimationProps)


class FluidCollidersList(PropertyGroup, FLUIDLAB_CommonList):

    def list_index_update(self, context:Context) -> None:
        item = self.active
        if not item:
            return
        
        colliders_obs = [c_ob.ob for c_ob in item.colliders] 
        if not colliders_obs:
            return
        
        bpy.ops.object.select_all(action='DESELECT')

        # Se seleccionan y se hace activo el primer collider:
        for c_ob in colliders_obs:
            c_ob.select_set(True)
        set_active_object(context, colliders_obs[0])
    
    list_index: IntProperty(name="Layer List", description="The Layer List", default=-1, update=list_index_update)
    list: CollectionProperty(type=FluidCollidersListItem)

    # los pointerproperty no pueden estar en el propio operador
    def filter_colliders_coll(self, coll):

        if coll.name != "RigidBodyWorld" and coll.name != FluidLabNaming.FluidLab and coll.users > 0 and FluidLabNaming.FluidLab not in coll:
            return True

        return False
    colliders_coll: PointerProperty(type=Collection, poll=filter_colliders_coll)


    def add_item(self, item_id:str, label_txt:str, c_obs:List[Object], item_type:str, single_mode:bool, gn_mode:bool) -> FluidCollidersListItem:

        item = self.list.add()
        item.id_name = item_id
        item.label_txt = label_txt
        item.item_type = item_type
        item.single_mode = single_mode
        item.gn_mode = gn_mode

        if len(c_obs) > 0:
            [item.add_collider(ob) for ob in c_obs]
        
        # seteamos el ultimo elemento como activo:
        self.list_index = self.length-1
        
        return item

    @property
    def get_current_type(self):
        return self.list[self.list_index].item_type
    
    @property
    def get_current_colliders(self):
        item = self.list[self.list_index]
        if item:
            return [c.ob for c in item.colliders]

    @property
    def get_all_colliders(self):
        return [c.ob for item in self.list for c in item.colliders]    
